/*
* @file_name: s3client.hpp
* @date: 2021/12/06
* @author: oaho
* Copyright @ hz oaho, All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef OSS_AWS_S3_CLIENT_HPP
#define OSS_AWS_S3_CLIENT_HPP
#include <memory>
#include <oss/aws/impl/policy_base.hpp>
#include <aws/core/auth/AWSCredentialsProvider.h>

#include <Util/logger.h>
namespace aws{
    class s3{
    public:
        using Ptr = std::shared_ptr<s3>;
    public:
        /*
         * @param endpoint: 0.0.0.0:9000
         * @param access key: key
         * @param secret: access_secret
         * */
        s3(const std::string& endpoint, const std::string& access_key, const std::string& access_secret){
            Aws::InitAPI(options);
            connect(endpoint, access_key, access_secret);
        }
        ~s3(){
            Aws::ShutdownAPI(options);
        }
    public:
        void connect(const std::string& endpoint, const std::string& access_key, const std::string& access_secret){
            client_config.reset(new Aws::Client::ClientConfiguration);
            try{
                cred.reset(new Aws::Auth::AWSCredentials(access_key, access_secret));
                client_config->endpointOverride = endpoint;
                client_config->enableTcpKeepAlive = true;
                client_config->scheme = Aws::Http::Scheme::HTTP;
                client_ptr.reset(new Aws::S3::S3Client(*cred, *client_config,
                                                       Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy::Never,
                                                       false,
                                                       Aws::S3::US_EAST_1_REGIONAL_ENDPOINT_OPTION::NOT_SET));
                if(!client_ptr)throw std::bad_alloc();
                this->endpoint = endpoint;
                this->access_key = access_key;
                this->access_secret = access_secret;
            }catch(const std::exception& e)
            {
                using namespace toolkit;
                ErrorL << e.what();
            }
        }
    public:
        template<typename policy, typename T>
        void put_object(const std::string& bucket, const std::string& object, T&& val, typename policy_base::PutObjectResponseReceivedHandler&& f){
            using request_type = typename policy::put_object_request_type;
            policy p;
            request_type request;
            std::shared_ptr<Aws::IOStream> content = std::make_shared<std::stringstream>();
            content->write(val.data(), val.size());
            request.WithBucket(bucket).WithKey(object);
            request.SetBody(content);
            p(client_ptr.get(), request, std::move(f));
        }

        template<typename policy> void get_object(const std::string& bucket,const std::string& object, typename policy_base::GetObjectResponseReceivedHandler&& f){
            using request_type = typename policy::get_object_request_type;
            policy p;
            request_type request;
            request.WithBucket(bucket).WithKey(object);
            p(client_ptr.get(), request, std::move(f));
        }

        template<typename policy> void del_object(const std::string& bucket, const std::string& object, typename policy_base::DeleteObjectResponseReceivedHandler&& f){
            using request_type = typename policy::get_object_request_type;
            policy p;
            request_type request;
            request.WithBucket(bucket).WithKey(object);
            p(client_ptr.get(), request, std::move(f));
        }
    private:
        std::unique_ptr<Aws::Client::ClientConfiguration> client_config;
        Aws::SDKOptions options;
        std::unique_ptr<Aws::Auth::AWSCredentials> cred;
        std::unique_ptr<Aws::S3::S3Client> client_ptr;
        std::string endpoint;
        std::string access_key;
        std::string access_secret;
    };
};
#endif